library pda.util;

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:io';
import 'package:dio/dio.dart';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:yaml/yaml.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'package:uuid/uuid.dart';
import 'package:flutter/services.dart';

/// 弹出框
showSuccess(BuildContext context, String title) {
  Alert(
      context: context,
      type: AlertType.success,
      title: title,
      style: AlertStyle(
          animationType: AnimationType.fromBottom, isOverlayTapDismiss: false),
      buttons: [
        DialogButton(
            child: Text(
              "确定",
              style: TextStyle(color: Colors.white, fontSize: 20),
            ),
            onPressed: () => Navigator.pop(context))
      ]).show();
}

showAlert(BuildContext context, String title) {
  Alert(
      context: context,
      type: AlertType.warning,
      title: title,
      style: AlertStyle(
          animationType: AnimationType.fromBottom, isOverlayTapDismiss: false),
      buttons: [
        DialogButton(
            child: Text(
              "确定",
              style: TextStyle(color: Colors.white, fontSize: 20),
            ),
            onPressed: () => Navigator.pop(context))
      ]).show();
}

showError(BuildContext context, String title) {
  Alert(
      context: context,
      type: AlertType.error,
      title: title,
      style: AlertStyle(
          animationType: AnimationType.fromTop, isOverlayTapDismiss: false),
      buttons: [
        DialogButton(
            child: Text(
              "确定",
              style: TextStyle(color: Colors.white, fontSize: 20),
            ),
            onPressed: () => Navigator.pop(context))
      ]).show();
}

/// ~弹出框

/// 全局初始化
Future<bool> init() async {
  debugPrint('初始化系统设置');
  await Setting._init();
  debugPrint('初始化系统设置完成');
  debugPrint('初始化数据库');
  await SqliteHelper.instance._init(rootBundle, !true);
  debugPrint('初始化数据库完成');
  return true;
}

/// 系统设置
class Setting {
  static SharedPreferences _prefs;
  static Setting _setting;

  String server = '192.168.1.120';
  int port = 8080;
  String _servletContext = '/hyts';

  String get servletContext => _servletContext;

  String get httpBaseUrl => 'http://$server:$port$servletContext';

  static Future<Setting> _init() async {
    if (_setting != null) {
      return _setting;
    }
    _prefs = await SharedPreferences.getInstance();
    _setting = Setting._();
    _setting.reload();
    return _setting;
  }

  Setting._();

  factory Setting.fromPref() {
    return _setting;
  }

  void reload() {
    server = _prefs.getString('server') ?? '';
    port = _prefs.getInt('port') ?? 0;
  }

  void save() {
    _prefs.setString('server', server);
    _prefs.setInt('port', port);
    if (!Platform.isAndroid) {
      // 在Android平台在set之后自动commit
      _prefs.commit();
    }
  }
}

/// ~系统设置

/// http请求
/// flutter 中解析json需要有固定的结构, 此类表示对服务端请求的结果
class Result {
  final bool success;
  final String message;
  int code;
  dynamic data;

  Result(this.success, [this.message = '']);

  String toJSONString() => json.encode(this);

  static Result fromJSONString(String jsonString) {
    var map = json.decode(jsonString);
    var success = map['success'];
    var code = map['code'];
    var message = map['message'];
    var data = map['data'];
    return Result(success, message)
      ..code = code
      ..data = data;
  }

  static Result fromJson(Map<String, dynamic> map) {
    var success = map['success'];
    var code = map['code'];
    var message = map['message'];
    var data = map['data'];
    return Result(success, message)
      ..code = code
      ..data = data;
  }

  ///  json.encode(obj)调用时的callback方法
  Map<String, dynamic> toJson() {
    Map<String, dynamic> rtn = {};
    rtn['success'] = success ?? false;
    rtn['message'] = message;
    rtn['code'] = code;
    rtn['data'] = data;
    return rtn;
  }

  @override
  String toString() {
    return 'Result: success=$success, code=$code, message=$message';
  }
}

typedef Function OnResult(Result result);
typedef Function OnData(Stream data);

class DownloadTransformer extends DefaultTransformer {
  @override
  Future<Stream> transformResponse(
      Options options, HttpClientResponse response) async {
//    options.extra["cookies"] = response.cookies;
    return response.transform(utf8.decoder);
  }
}

class DioHttp {
  final Dio dio = Dio(Options(
      baseUrl: "http://www.dtworkroom.com/doris/1/2.0.0/",
      // 15 seconds
      connectTimeout: 15 * 1000,
      receiveTimeout: 30 * 1000,
      // 30 seconds
      headers: {
        "user-agent":
            'Mozilla/5.0 (Linux; Android; wv) AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/66.0.3359.126 handheled;device_id/HUAWEI-8317001280-38378BB4CF7A',
        "version": "1902-a001"
      },
      contentType: ContentType.json,
      // Transform the response data to a String encoded with UTF8.
      // The default value is [ResponseType.JSON].
      responseType: ResponseType.PLAIN));

  static DioHttp _instance = DioHttp._();

  DioHttp._() {
    debugPrint('DioHttp: ${this.hashCode}');
  }

  static DioHttp instance([baseUrl]) {
    baseUrl ??= Setting.fromPref().httpBaseUrl;
    assert(baseUrl != null && baseUrl.length > 7);
    _instance.dio.options.baseUrl = baseUrl;
    return _instance;
  }

  static String _errorMessage(DioError err, [String defaultMessage = '未知错误']) {
    String msg = defaultMessage;
    switch (err.type) {
      case DioErrorType.CANCEL:
        msg = '请求被取消';
        break;
      case DioErrorType.CONNECT_TIMEOUT:
        msg = '连接超时';
        break;
      case DioErrorType.DEFAULT:
        msg = '不能连接到服务器';
        break;
      case DioErrorType.RECEIVE_TIMEOUT:
        msg = '数据接收超时';
        break;
      case DioErrorType.RESPONSE:
        msg = '服务端响应错误:${err.response.statusCode}';
        break;
      default:
        msg = err.message;
    }
    return msg;
  }

  _handleResponse(Future<Response> future, OnResult onResult) {
    future.then((response) {
      final path = response.request.path;
      debugPrint('请求$path成功, 正在解析response.');
      var result = Result.fromJSONString(response.data.toString());
      debugPrint('请求$path成功, 解析response完成.');
      if (onResult != null)
        onResult(result);
      else
        debugPrint('result of get $path:${result.toString()}');
    }).catchError((_) {
      debugPrint(_.toString());
      var msg = '请求失败';
      if (_ is DioError) msg = _errorMessage(_);
      if (onResult != null) {
        onResult(Result(false, msg));
      }
    });
  }

  get(String path,
      {Map<String, String> params = const {}, OnResult onResult}) async {
    if (path[0] != '/') path = '/$path';
    _handleResponse(dio.get(path, data: params), onResult);
  }

  downLoadToDb(String path,
      {Map<String, String> params = const {}, OnResult onResult}) async {
    if (path[0] != '/') path = '/$path';
    var response = await dio.get(path, data: params);
//    _handleResponse(dio.get(path, data: params), onResult);
  }

  get2(String path,
      {Map<String, String> params = const {}, OnResult onResult}) async {
    if (path[0] != '/') path = '/$path';
    var transformer = dio.transformer;
    dio.transformer = DownloadTransformer();
    var response = await dio.get(path, data: params);
//    onData(response.data);
    dio.transformer = transformer;
//    _handleResponse(dio.get(path, data: params), onResult);
  }

  post(String path,
      {Map<String, String> params = const {}, OnResult onResult}) async {
    if (path[0] != '/') path = '/$path';
    _handleResponse(dio.post(path, data: params), onResult);
  }

  /// 下载较大的数据
  download(String path,
      {Map<String, String> params = const {}, OnData onData}) async {
    if (path[0] != '/') path = '/$path';
    var transformer = dio.transformer;
    dio.transformer = DownloadTransformer();
    var response = await dio.get(path, data: params);
    onData(response.data);
    dio.transformer = transformer;
//    var extPath = (await getExternalStorageDirectory()).path;
//    var dir = Directory('$extPath/hytsoft');
//    dir.createSync(recursive: true);
//    var response = await dio.download(path, '$extPath/hytsoft/download.json',
//        options: Options(responseType: ResponseType.STREAM));
  }
}

/// ~http请求

/// sqlite 操作
const String DB_NAME = 'pdaclient.db';

class SqlMap {
  final Map<String, List<String>> _sqls = {};

  SqlMap(String yaml) {
    assert(yaml != null);
    var root = loadYamlNode(yaml);
    if (root is YamlList) {
      for (var sql in root) {
        add('_', sql);
      }
    } else if (root is YamlMap) {
      YamlMap map = root as YamlMap;
      if (map.containsKey('versions')) {
        YamlList versions = map['versions'];
        versions.forEach((version) {
          YamlList sqls = map[version];
          for (var sql in sqls) {
            add('$version', sql);
          }
        });
      } else {
        for (var name in map.keys) {
          add(name, map[name]);
        }
      }
    }
  }

  bool get isEmpty => _sqls.isEmpty;

  int get size => _sqls.length;

  bool contains(String name) => _sqls.containsKey(name);

  List<String> sql(String name) => _sqls[name];

  Iterable<String> names() => _sqls.keys;

  SqlMap add(String name, String sql) {
    if (contains(name))
      _sqls[name].add(sql);
    else
      _sqls[name] = [sql];
    return this;
  }

  List<String> get(String name) {
    return _sqls[name];
  }
}

enum Tables { pda_user, pda_provider, pda_depart, pda_product }

const Map<Tables, String> TableNames = {
  Tables.pda_user: 'pda_user',
  Tables.pda_provider: 'pda_provider',
  Tables.pda_depart: 'pda_depart',
  Tables.pda_product: 'pda_product',
};

SqliteHelper _sqliteHelper;

SqliteHelper getSqliteHelper() {
  return _sqliteHelper;
}

class SqliteHelper {
  static const String CREATE_SQL = 'assets/sql/create.yaml';
  static const String CRUD_SQL = 'assets/sql/crud.yaml';
  String _path;
  Database _db;
  static bool _inited = false;
  static SqlMap _createSql;
  static SqlMap _crudSql;

  get dbPath => join(_path, DB_NAME);

  Batch get batch => _db.batch();

  get isInited => _inited;

  static final SqliteHelper instance = SqliteHelper._();

  static Future<Database> getDatabase() async {
    var _instance = instance ?? SqliteHelper._();
    if (_instance._path == null) {
      _instance._path = await getDatabasesPath();
      _instance._db = await openDatabase(_instance.dbPath);
    }
    return _instance._db;
  }

  SqliteHelper._();

  _init(AssetBundle sqlAsset, [bool force = false]) async {
    if (_inited) return;
    debugPrint('init sqlite');
//    var byteData = await sqlAsset.load(CREATE_SQL);
    var yaml = await sqlAsset.loadString(CREATE_SQL);
    _createSql = SqlMap(yaml);
    _path = await getDatabasesPath();
    // android 6 之前sqlite的路径为: /data/data/${package.name}/databases
    // 该路径一定存在
    // android 6 之后sqlite的路径为: /data/user/${userid}/${package.name}/databases
    // 该路径不一定存在
    var dir = Directory(_path);
    if (force) {
      if (dir.existsSync()) {
        dir.deleteSync(recursive: true);
      }
    }
    if (!dir.existsSync()) {
      // 目录不存在则创建
      dir.createSync(recursive: true);
    }
    debugPrint('dbPath:$dbPath');
    _db = await openDatabase(dbPath,
        version: 19020000, onCreate: _onCreate, onUpgrade: _onUpgrade);
    sqlAsset.loadString(CRUD_SQL).then((yaml) => _crudSql = SqlMap(yaml));
    _inited = true;
    _sqliteHelper = this;
  }

  _onCreate(Database db, int version) {
    for (var name in _createSql.names()) {
      debugPrint('execute sql: $name');
      _createSql.get(name).forEach((sql) {
        db.execute(sql);
      });
    }
  }

  _onUpgrade(Database db, int oldVersion, int version) {
    for (var name in _createSql.names()) {
      if (int.parse(name) >= version) {
        debugPrint('execute sql: $name');
        _createSql.get(name).forEach((sql) {
          db.execute(sql);
        });
      }
    }
  }

  delete(Tables table, String where, List<dynamic> args) {
    debugPrint('delete from ${TableNames[table]}  where $where');
    _db.delete(TableNames[table], where: where, whereArgs: args);
  }

  batchWork() {}

  insertBatch(Tables table, List<dynamic> data) {
    if (data == null || data.isEmpty) {
      return;
    }
    for (var item in data) {
      batch.insert(TableNames[table], item);
    }
    batch.commit(noResult: true, continueOnError: false);
  }

  insertWithSql(String sql, List<dynamic> args, bool commit) async {
    if (args == null || args.isEmpty) {
      return;
    }
    batch.rawInsert(sql, args);
    if (commit) {
      await batch.commit(noResult: true, continueOnError: false);
    }
  }

  Future<Map> fetch(Tables table, String id) async {
    List<Map> list = await _db.rawQuery(
        'select * from ${TableNames[table]} where id = ? limit 1', [id]);
    return list == null || list.isEmpty ? null : list.first;
  }
}

loadYaml(String yaml) {
  assert(yaml != null);
  return loadYamlNode(yaml);
}

DateFormat yyyyMMdd = DateFormat('yyyy-MM-dd');
DateFormat yyyyMMddHHmmss = DateFormat('yyyy-MM-dd HH:mm:ss');

String formatDateOnly(DateTime dt) {
  return yyyyMMdd.format(dt);
}

DateTime parseDateOnly(String date) {
  return yyyyMMdd.parse(date);
}

String formatDateTime(DateTime dt) {
  return yyyyMMddHHmmss.format(dt);
}
